﻿using System.Collections.Generic;
using System.Linq;
using Windows.System;
using LinkPad.Shapes;
using Windows.Devices.Input;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;

namespace LinkPad.Handlers
{
    class TextMover : ICanvasActionPublicHandler, IHasColor, IHasFont, ISubEditor
    {
        #region Nested types

        private class SelectedTextInfo
        {
            // Original colour
            public Color FillColor;

            public double OrigX;
            public double OrigY;
        }

        #endregion

        #region Fields

        private readonly ShapeManager _shapeManager;
        private readonly Canvas _canvas;

        private readonly Color _normalColor = Colors.Yellow;
        private readonly Color _selectedColor = Colors.Green;

        private Point _pressPoint;
        private uint _penId;

        private readonly Dictionary<ManagedText, SelectedTextInfo> _selectedTexts
            = new Dictionary<ManagedText, SelectedTextInfo>();

        #endregion

        #region Constructors

        public TextMover(ShapeManager shapeManager)
        {
            _shapeManager = shapeManager;
            _canvas = _shapeManager.Canvas;
            MultiSelectStatus = MultiSelectStatus.NonMulti;
        }

        #endregion

        #region Properties

        #region IHasColor Properties

        public bool HasColor
        {
            get
            {
                return (FirstSelectedTextInfo != null);
            }
        }

        public Color MainColor
        {
            get
            {
                return FirstSelectedTextInfo == null ? new Color() : FirstSelectedTextInfo.FillColor;
            }
            set
            {
                foreach (var v in _selectedTexts)
                {
                    v.Value.FillColor = value;
                }
            }
        }


        #endregion

        #region ISubEditor

        public Editor Parent
        {
            get; set;
        }

        public bool HasSelected
        {
            get
            {
                return _selectedTexts.Count > 0;
            }
        }

        #endregion

        #region IHasFont Properties

        public bool HasFont { get { return FirstSelectedText != null; } }

        public double FontSize
        {
            get
            {
                if (FirstSelectedText == null) return ManagedText.DefaultFontSize; // default size; this should never be used anyway
                return FirstSelectedText.FontSize;
            }
            set
            {
                foreach (var v in _selectedTexts)
                {
                    v.Key.FontSize = value;
                }
            }
        }

        #endregion

        public MultiSelectStatus MultiSelectStatus { get; set; }

        private ManagedText FirstSelectedText
        {
            get
            {
                return _selectedTexts.Count > 0 ? _selectedTexts.Keys.First(linkInfo => true) : null;
            }
        }

        private SelectedTextInfo FirstSelectedTextInfo
        {
            get
            {
                return _selectedTexts.Count > 0 ? _selectedTexts.Values.First(linkInfo => true) : null;
            }
        }

        #endregion

        #region Methods

        #region ICanvasActionPublicHandler members

        public void HandlePointerPressed(object sender, PointerRoutedEventArgs e)
        {
            var pt = e.GetCurrentPoint(_canvas);

            TrySelect(pt.Position, e.KeyModifiers == VirtualKeyModifiers.Control);

            if (_selectedTexts.Count == 0)
            {
                return;
            }

            var pointerDevType = e.Pointer.PointerDeviceType;

            if (pointerDevType == PointerDeviceType.Pen ||
                pointerDevType == PointerDeviceType.Mouse &&
                pt.Properties.IsLeftButtonPressed
                || pointerDevType == PointerDeviceType.Touch)
            {
                _pressPoint = pt.Position;

                foreach (var p in _selectedTexts)
                {
                    var text = p.Key;
                    var info = p.Value;
                    info.OrigX = text.X;
                    info.OrigY = text.Y;
                }

                _penId = pt.PointerId;
            }
            // NOTE in case that the handled flag should not be used for indicating the 
            //      selection we should add a separate flag (such as HasObjectSelected) 
            //      to ICanvasActionPublicHandler for that purpose
            e.Handled = true;
        }

        public void HandlePointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (e.Pointer.PointerId != _penId)
            {
                return;
            }

            var pt = e.GetCurrentPoint(_canvas);

            var deltaX = pt.Position.X - _pressPoint.X;
            var deltaY = pt.Position.Y - _pressPoint.Y;

            foreach (var p in _selectedTexts)
            {
                var text = p.Key;
                var info = p.Value;
                text.X = info.OrigX + deltaX;
                text.Y = info.OrigY + deltaY;
            }
        }

        public void HandlePointerReleased(object sender, PointerRoutedEventArgs e)
        {
            _penId = 0;
        }

        public void HandlePointerExited(object sender, PointerRoutedEventArgs e)
        {
        }

        public void HandleCanvasTapped(object sender, TappedRoutedEventArgs e)
        {
            // not doing anything here to avoid interfering with multiple selection
        }

        public void HandleCanvasDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
        {
        }

        public void HandleDeactivated()
        {
            DeselectAll();
        }

        public void DeselectAll()
        {
            foreach (var p in _selectedTexts)
            {
                var text = p.Key;
                var info = p.Value;
                text.Fill = new SolidColorBrush(info.FillColor);
            }
            _selectedTexts.Clear();
        }

        public void DeleteAllSelected()
        {
            foreach (var selected in _selectedTexts.Keys)
            {
                _shapeManager.RemoveShape(selected);
            }
        }

        #endregion

        private void TrySelect(Point pt, bool multiSelectKey = false)
        {
            ManagedText selected = null;
            foreach (var shape in _shapeManager.Shapes)
            {
                var text = shape as ManagedText;
                if (text != null && text.BoundingBox.Contains(pt))
                {
                    selected = text;
                    break;
                }
            }

            if (multiSelectKey)
            {
                MultiSelectStatus = MultiSelectStatus.Multi;
            }
            else if (selected == null)
            {
                MultiSelectStatus = MultiSelectStatus.Empty;
            }
            else if (!_selectedTexts.ContainsKey(selected))
            {
                DeselectAll();
                MultiSelectStatus = MultiSelectStatus.NonMultiResetAll;
            }

            if (selected != null)
            {
                SelectText(selected);
            }
        }

        private void SelectText(ManagedText textBox)
        {
            if (_selectedTexts.ContainsKey(textBox))
            {
                return;
            }
            var fillBrush = textBox.Fill as SolidColorBrush;
            _selectedTexts.Add(textBox,
                 new SelectedTextInfo
                 {
                     FillColor = fillBrush != null? fillBrush.Color : _normalColor
                 });

            textBox.Fill = new SolidColorBrush(_selectedColor);
        }

        #endregion
    }
}
